#ifndef __RED_MAP_OBJECT_H__
#define __RED_MAP_OBJECT_H__

#include <stdint.h>
#include "thread.h"
#include "redobject.h"
#include "redexceptionobject.h"

#define RED_POOL_INTERN_MAX		16
#define RED_MAP_BUCKET_SIZE		128

typedef struct _RedMapNode RedMapNode;
typedef struct _RedMapNode
{
	RedMapNode		*prev;
	RedMapNode		*next;

	RedMapNode		*lprev;
	RedMapNode		*lnext;

	uint32_t		 hash;
	RedObject		*key;
	RedObject		*value;
} RedMapNode;

typedef struct _RedPoolNode RedPoolNode;
typedef struct _RedPoolNode
{
	RedPoolNode		*prev;
	RedPoolNode		*next;
	RedPoolNode		*lprev;
	RedPoolNode		*lnext;
	RedObject		*value;

	union
	{
		int64_t		 key_i64;

		struct
		{
			int		 key_strl;
			char	 key_strp[RED_POOL_INTERN_MAX];
			uint32_t key_strhash;
		};
	};
} RedPoolNode;

typedef struct _RedMapObject
{
	RedObject_HEAD

	int				 mp_nmods;
	int				 mp_count;
	RedMutex		 mp_mutex;
	char			 mp_strkey;
	char			 mp_intern;

	union
	{
		RedMapNode	*mp_nodes;
		RedPoolNode	*mp_pools;
	};
	union
	{
		RedMapNode	 mp_nodelist;
		RedPoolNode	 mp_poollist;
	};
} RedMapObject;

typedef char (* RedMapEnumerator)(RedObject *key, RedObject *value, void *context);

RED_EXPORT extern RedTypeObject RedType_Map;

#define RedMap_Exact(o)				((o)->ob_type == &RedType_Map)
#define RedMap_NMods(o)				(((RedMapObject *)(o))->mp_nmods)
#define RedMap_Count(o)				(((RedMapObject *)(o))->mp_count)
#define RedMap_StrKey(o)			(((RedMapObject *)(o))->mp_strkey)
#define RedMap_Intern(o)			(((RedMapObject *)(o))->mp_intern)

#define RedMap_Nodes(o)				(((RedMapObject *)(o))->mp_nodes)
#define RedMap_NodeList(o)			(&(((RedMapObject *)(o))->mp_nodelist))

#define RedMap_Pools(o)				(((RedMapObject *)(o))->mp_pools)
#define RedMap_PoolList(o)			(&(((RedMapObject *)(o))->mp_poollist))

#define RedMap_Mutex(o)				(&(((RedMapObject *)(o))->mp_mutex))
#define RedMap_Unlock(o)			RedMutex_Release(RedMap_Mutex(o))
#define RedMap_ReadLock(o, m)					\
	do											\
	{											\
		RedMutex_Acquire(RedMap_Mutex(o));		\
		(m) = RedMap_NMods(o);					\
	} while (0)

#define RedMap_WriteLock(o, m)					\
	do											\
	{											\
		RedMutex_Acquire(RedMap_Mutex(o));		\
		(m) = ATOMIC_INC(RedMap_NMods(o));		\
	} while (0)

#define RedMap_CheckMods(o, m)					\
	({											\
		char _ok = (RedMap_NMods(o) == (m));	\
		if (!_ok)								\
		{										\
			RedMap_Unlock(o);					\
			RedException_ThrowFormat(			\
				&RedExc_RuntimeError,			\
				"Modified during operation");	\
		}										\
		_ok;									\
	})

RED_EXPORT RedObject *RedMap_Create(void);
RED_EXPORT RedObject *RedMap_CreateStringKey(void);

RED_EXPORT char RedMap_Del(RedObject *self, RedObject *key);
RED_EXPORT char RedMap_Put(RedObject *self, RedObject *key, RedObject *value);
RED_EXPORT RedObject *RedMap_Get(RedObject *self, RedObject *key);

/* these del/get/set functions are for internal using, and supports string-keys only */
RED_EXPORT char RedMap_DelString(RedObject *self, RedObject *key);
RED_EXPORT char RedMap_PutString(RedObject *self, RedObject *key, RedObject *value);
RED_EXPORT RedObject *RedMap_GetString(RedObject *self, RedObject *key);

RED_EXPORT RedObject *RedMap_Keys(RedObject *self);
RED_EXPORT RedObject *RedMap_Items(RedObject *self);
RED_EXPORT RedObject *RedMap_Values(RedObject *self);

RED_EXPORT void RedMap_Clear(RedObject *self);
RED_EXPORT RedObject *RedMap_Clone(RedObject *self);
RED_EXPORT RedObject *RedMap_Concat(RedObject *self, RedObject *other);	/* this one will create a new map */
RED_EXPORT RedObject *RedMap_Extend(RedObject *self, RedObject *other);		/* this one doesn't, it returns itself of ref-count increased by 1 */

RED_EXPORT char RedMap_HasKey(RedObject *self, RedObject *key);
RED_EXPORT char RedMap_Foreach(RedObject *self, RedMapEnumerator callback, void *context);

/* internal functions, grouped by key type, each group are NOT compatiable with others (see `RedPoolNode` above)
 * these functions are specially designed for constant pool, they do NOT perform any parameter checking
 * DO NOT use in your module, unless you know exactly what you are doing
 */

RED_EXPORT RedObject *RedPool_Create(void);

/* for integer constant pool and thread manager */
RED_EXPORT char RedPool_DelInt(RedObject *self, int64_t key);
RED_EXPORT void RedPool_PutInt(RedObject *self, int64_t key, RedObject *value);
RED_EXPORT RedObject *RedPool_GetInt(RedObject *self, int64_t key);

/* for string constant pool */
RED_EXPORT void RedPool_PutString(RedObject *self, const char *s, int len, RedObject *value);
RED_EXPORT RedObject *RedPool_GetString(RedObject *self, const char *s, int len);

#endif /* __RED_MAP_OBJECT_H__ */
